/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/


#include "sky_impl.h"
#include "performance/factory.h"
#include <string>

using namespace atc;

//------------------------------------------------------------------------------

/*!
 * Default Constructor
 */
SkyImpl::SkyImpl() {}

/*!
 * Destructor
 */
SkyImpl::~SkyImpl() {}

//------------------------------------------------------------------------------

//
// Sky Interface [creational]
// (see atc_sky.h for documentation)
//

void SkyImpl::create_aircraft( const char * id, const char * type ) {
	// @todo: 
	// validate performance data exists
	performance::Factory::performance( type );

	std::pair< AircraftParamMapIt, bool > result = _aircraft.insert(
		std::make_pair( id, AircraftParam( id, type ) )
	);
	if(!result.second)
		throw sky_error("error creating aircraft");
}


void SkyImpl::set_aircraft_launch( const char *id, unsigned int launch ) {
	atc::AircraftParamMapIt it = findAircraft(id);
	it->second.launch = launch;
}

void SkyImpl::add_aircraft_pathdescriptor(
		const char *id,
		double x, double y,
		double tz, double dz,
		double tv, double dv
){
	AircraftParamMapIt it = findAircraft( id );
	FlightPath &rds = it->second.flightpath;
	rds.push_back( FlightPathDesc( x, y, tz, dz, tv, dv ) );
}

//void atc::SkyImpl::add_aircraft_pathdescriptor(
//	const char * id, 
//	double x, double y, double tz, double tv
//){
//	add_aircraft_pathdescriptor(
//		id, x, y, tz, atc::NaN, tv,atc::NaN
//	);
//}

void SkyImpl::set_aircraft_initialaltitude( const char * id, double z ) {
	AircraftParamMapIt it = findAircraft(id);
	it->second.oAltitude = z;
}

void SkyImpl::set_aircraft_initialvelocity( const char * id, double v ) {
	AircraftParamMapIt it = findAircraft(id);
	it->second.oVelocity = v;
}

void SkyImpl::set_aircraft_closedpath( const char * , bool ) {
	// \todo: closed loop path
//	atc::AircraftParamMapIt it = findAircraft(id);
//	it->second.loop = loop;
}

void SkyImpl::create_weather( const char *uid ) {
	std::string id( uid );
	try {
		if ( !strlen( uid ) ) {
			throw sky_error( "invalid unique area identifier [ " + id + " ]" );
		}
		if ( _area_at.count( uid ) ) {
			throw sky_error( "area identifier NOT unique [ " + id + " ]" );
		}

		std::pair< AreaHash::const_iterator, bool > result =
			_area_at.insert( std::make_pair(
				id, AreaDefinition( uid )
			));

		if ( ! result.second ) {
			throw sky_error( "cannot create area [ " + id + " ]" );
		}
	}
	catch ( sky_error &e ) {
		throw e;
	}
}

void SkyImpl::add_weather_vertex( const char *id, double x, double y ) {
	validate( id );
	_area_at[ id ].descriptors.push_back( new VertexDescriptor( x, y ) );
}

void SkyImpl::add_weather_arc( const char *id, double x, double y, double r ) {
	validate( id );
	_area_at[ id ].descriptors.push_back( new ArcDescriptor( x, y, r ) );
}

void SkyImpl::add_weather_ellipse( 
		const char *id
	  , double x, double y
	  , double w, double h
	  , double a
){
	validate( id );
	_area_at[ id ].descriptors
		.push_back( new EllipseDescriptor( x, y, w, h, a ) );
	;
}

void SkyImpl::add_conflicts( const ConflictDeclarationMap &cdm ) {
	ConflictDeclarationMap::const_iterator it = cdm.begin();
	for ( ; it != cdm.end(); ++it )
			_conflicts.insert( *it );
}

//------------------------------------------------------------------------------

/*!
 */
AircraftParamMapIt SkyImpl::findAircraft( const char *id ) {
	atc::AircraftParamMapIt it = _aircraft.find(id);
	if(it == _aircraft.end())
		throw sky_error(
			std::string(id) + " aircraft does not exist"
		);
	return it;
}


/*!
 * Confirm Area has been declared
 */
void SkyImpl::validate( const char *id ) {
	try {
		if ( !_area_at.count( id ) ) {
			throw sky_error( 
					"Area is NOT defined [ " 
				  + std::string( id ) + " ]!" 
			);
		}
	}
	catch ( sky_error &e ) {
		throw e;
	}
}
//------------------------------------------------------------------------------
